package com.study.kt._01grammar._02oop

fun main() {
    //定义Int类型的Box，自动推断 <Int> 可以省略，java中不指定则为Object
    val intBox = Box<Int>(1)
    intBox.printType()

    //调用泛型函数
    printType(1)
    printType("fauks")
    printType(false)

    val array = arrayListOf("a", "b", "c", "d")
    println(array.indices)
}

//定义一个带泛型的类
open class Box<T>(var thing: T) {
    fun printType() {
        println(thing)
    }
}

//继承泛型类，可以指明泛型类型
class StringBox(thing: String) : Box<String>(thing)

//也可以不指明泛型，重新定义泛型传递给父类
class UnSignBox<D>(thing: D) : Box<D>(thing)

//定义泛型函数 不知道参数是什么类型 可以定义一个泛型 <T>声明一个泛型T
fun <T> printType(thing: T) {
    when (thing) {
        is Int -> println("是Int")
        is String -> println("是String")
        else -> println("未知类型")
    }
}

/*协变、逆变
* Java中 List<Object> List<String> 相互赋值是不允许的，编译报错，所以java推出通配符 List<? extends E>
        List<String>是List<? extends Object>的子类型，这就是协变，我们把泛型看成<? extends Object>而不是String实际类型，这种做法是安全的

       * List<? super String> 为逆变
       * 生产者只读不可写 使用 ? extends E,例如? extends Object,取出来一定是Object，而真是类型未知，可能是Date、可能是String 写入会报错
       * 消费者只写不可读 使用 ? super E：例如? super A，存入子类A肯定不报错，而取出来的类型未知 会报错
       *
  kotlin：声明处协变 java：使用处协变
* */
// kotlin中的协变与逆变
// 对于t只有get，表示生产者使用out 表示出去,m 只有set表示消费者使用in，形象
class MyClassGen<out T, in M>(t: T, m: M) {
    private var t: T
    private var m: M

    init {
        this.t = t
        this.m = m
    }

    fun get(): T = this.t

    fun set(m: M) {
        this.m = m
    }
}

fun testCovariantAndControvariant(myClass: MyClassGen<String, Number>) {
    val myClz: MyClassGen<Any, Int> = myClass
}

// 类型投影,参数中类型in、out
fun testTypeProjection() {
    val from = arrayOf(1, 2, 3)
    val to = arrayOf<Any>("a", "b", "c")
    copy(from, to)

    val target = arrayOf<Any>(1, 2)
    setValue(target, 0, "a")
}

// 如果此处声明Array<Any> 编译报错，因为from为Array<Int>类型与Array<Any>不匹配
// 从安全角度考虑，from为Array<Int>类型，可以通过out 避免写入非Int类型而改为只读数据
fun copy(from: Array<out Any>, to: Array<Any>) {
    for (i in from.indices) {
        to[i] = from[i]
    }
}

//Array<String>与要传入target类型Array<Int>不匹配
// 由于要写入数据，避免类型转换错误，子类写入父类肯定是安全的
fun setValue(target: Array<in String>, index: Int, value: String) {
    target[index] = value
}

// 星投影 User<* >

// 多个上界
class UpperBoundsClass<T> where T : Comparable<T>, T : Any


//reified表示获取T的类型，如果直接为T则获取不到
//需要内联函数支持
inline fun <reified T> getClassName(): String? {
    return T::class.simpleName
}
